#include "FloatingTextManager.h"

FloatingTextManager::FloatingTextManager(void)
{
}

FloatingTextManager::~FloatingTextManager(void)
{
	mSprite->Release();
}

FloatingTextManager::FloatingTextManager(const FloatingTextManager &mm)
{
}

FloatingTextManager* FloatingTextManager::operator=(const FloatingTextManager &mm)
{
	return this;
}

FloatingTextManager* FloatingTextManager::Instance()
{
	static FloatingTextManager instance;

	return &instance;
}

void FloatingTextManager::Initialize()
{
	mNextPersistentTextID = 0;
	D3DXCreateSprite(gD3DDevice, &mSprite);
}

void FloatingTextManager::OnResetDevice(int bbwidth, int bbheight)
{
	mSprite->OnResetDevice();
	mBBWidth = bbwidth;
	mBBHeight = bbheight;
}

void FloatingTextManager::OnLostDevice()
{
	mSprite->OnLostDevice();
}

void FloatingTextManager::Update(float dt)
{
	Texts::iterator t = textData.begin();
	while (t != textData.end())
	{
		// decrement the delay
		(*t)->delay -= dt;

		bool deleted = false;

		// if the delay is finished, start updating for real
		if ((*t)->delay < 0)
		{

			switch((*t)->textType)
			{
			case FLTTXTTYPE_PERSISTENT:
				// nothing special, just keep rendering at the current location
				if ((*t)->threeD)
					(*t)->position = ConvertToScreenCoords((*t)->realposition);	// only convert the position if it's not already in screen coords

				break;

			case FLTTXTTYPE_BOUNCE:
				(*t)->lifeTime -= dt;
				if ((*t)->lifeTime < 0.0f)
				{
					deleted = true;
					delete [TEXT_BUFFERSIZE] (*t)->text;
					delete *t;
					t = textData.erase(t);
				}
				else
				{
					if ((*t)->threeD)
						(*t)->position = ConvertToScreenCoords((*t)->realposition); // only convert the position if it's not already in screen coords

					if ((*t)->lifeTime > (*t)->maxLifeTime / 2)
					{
						(*t)->scale += (((*t)->maxScale - (*t)->minScale) / ((*t)->maxLifeTime)) * dt;
					}
					else
					{
						(*t)->scale -= (((*t)->maxScale - (*t)->minScale) / ((*t)->maxLifeTime)) * dt;
					}

				}

				break;

			case FLTTXTTYPE_RISING:
				(*t)->lifeTime -= dt;
				if ((*t)->lifeTime < 0.0f)
				{
					deleted = true;
					delete [TEXT_BUFFERSIZE] (*t)->text;
					delete *t;
					t = textData.erase(t);
				}
				else
				{
					if ((*t)->threeD)
					{
						(*t)->realposition += (*t)->velocity * dt; // use 3D coords
						(*t)->position = ConvertToScreenCoords((*t)->realposition);
					}
					else
					{
						(*t)->position += (*t)->velocity * dt; // use 2D coords
					}
				}

				break;

			}		
		}

		if (!deleted)
			t++;
	}
}


void FloatingTextManager::Render()
{
	mSprite->Begin(D3DXSPRITE_ALPHABLEND);
	// reset the sprite's matrix
	
	Texts::iterator t = textData.begin();
	while (t != textData.end())
	{
		// don't render if the delay is still active
		if ((*t)->delay < 0)
		{
			D3DXMATRIX S, T;
			float s = (*t)->scale;
			D3DXMatrixScaling(&S, s, s, 1.0f);
			D3DXMatrixTranslation(&T, (*t)->position.x, (*t)->position.y, 0.0f);
			mSprite->SetTransform(&(S*T));
			RECT R = {0, 0, 0, 0};
			(*t)->font->DrawTextA(mSprite, (*t)->text, -1, &R, DT_NOCLIP | DT_CENTER | DT_VCENTER, (*t)->color);
		}
		t++;
	}

	mSprite->End();
}

//////////////////////////////////////////////
// Bouncing Text
//
// 3D Text which expands, contracts and then disappears
//////////////////////////////////////////////
void FloatingTextManager::AddBouncingText(ID3DXFont *font, bool threeD, char* text, float lifeTime, D3DXVECTOR3 position, float minScale, float maxScale, D3DCOLOR color, float delay)
{
	TextData * td = new TextData;
	td->textType = FLTTXTTYPE_BOUNCE;
	td->threeD = threeD;
	td->font = font;
	td->maxLifeTime = lifeTime;
	td->lifeTime = lifeTime;
	td->delay = delay;
	
	if (threeD)
		td->position = ConvertToScreenCoords(position);
	else
		td->position = position;

	td->realposition = position;
	td->minScale = minScale;
	td->maxScale = maxScale;
	td->scale = minScale;
	td->text = CreateTextBuffer(text);
	td->color = color;

	textData.push_back(td);
}

//////////////////////////////////////////////
// PersistentText
// 
// 3D Text which remains on screen until otherwise notified
// Returns an ID number which can be called to disable the text
//////////////////////////////////////////////
int FloatingTextManager::AddPersistentText(ID3DXFont *font, bool threeD, char* text, D3DXVECTOR3 position, D3DCOLOR color, float delay)
{
	TextData * td = new TextData;
	td->textType = FLTTXTTYPE_PERSISTENT;
	td->threeD = threeD;
	td->font = font;
	td->delay = delay;

	if (threeD)
		td->position = ConvertToScreenCoords(position);
	else
		td->position = position;

	td->realposition = position;
	td->minScale = 1;
	td->maxScale = 1;
	td->scale = 1;
	td->text = CreateTextBuffer(text);
	td->color = color;
	td->id = mNextPersistentTextID++;

	textData.push_back(td);

	return td->id;
}

//////////////////////////////////////////////
// RisingText
//
// Text which starts at a position, rises until it's lifetime is over and then disappears
//////////////////////////////////////////////
void FloatingTextManager::AddRisingText(ID3DXFont *font, bool threeD, char* text, float lifeTime, float scale, D3DXVECTOR3 startPos, D3DXVECTOR3 velocity, D3DCOLOR color, float delay)
{
	TextData * td = new TextData;
	td->threeD = threeD;
	td->textType = FLTTXTTYPE_RISING;
	td->font = font;
	td->maxLifeTime = lifeTime;
	td->lifeTime = lifeTime;
	td->delay = delay;

	if (threeD)
		td->position = ConvertToScreenCoords(startPos);
	else
		td->position = startPos;

	td->realposition = startPos;
	td->velocity = velocity;
	td->scale = scale;
	td->text = CreateTextBuffer(text);
	td->color = color;

	textData.push_back(td);
}

//////////////////////////////////////////////
// DrawInstantText
//
// Draws the given text at the given location instantly.
// avoids having to use a persistent floating text object
//////////////////////////////////////////////
void FloatingTextManager::DrawInstantText(ID3DXFont *font, bool threeD, char* text, float scale, D3DXVECTOR3 position, D3DCOLOR color)
{
	mSprite->Begin(D3DXSPRITE_ALPHABLEND);
	// reset the sprite's matrix
	text = CreateTextBuffer(text);
	D3DXVECTOR3 pos;
	if (threeD)
	{
		pos = ConvertToScreenCoords(position);
	}
	else
	{
		pos = position;
	}
	D3DXMATRIX S, T;
	D3DXMatrixScaling(&S, scale, scale, scale);
	D3DXMatrixTranslation(&T, pos.x, pos.y, 0.0f);
	mSprite->SetTransform(&(S*T));
	RECT R = {0, 0, 0, 0};
	font->DrawTextA(mSprite, text, -1, &R, DT_NOCLIP | DT_CENTER | DT_VCENTER, color);
	
	mSprite->End();

	delete [TEXT_BUFFERSIZE] text;
}

//////////////////////////////////////////////
// CreateTextBuffer
//
// Creates a text buffer from the passed information
//////////////////////////////////////////////
char* FloatingTextManager::CreateTextBuffer(char* text)
{
	char *buffer = new char[TEXT_BUFFERSIZE];
	memcpy(buffer, text, TEXT_BUFFERSIZE);
	return buffer;
}

// converts the given position in 3D space into screen coordinates
D3DXVECTOR3 FloatingTextManager::ConvertToScreenCoords(D3DXVECTOR3 position)
{
	D3DXMATRIX m;
	D3DXVECTOR4 s;
	D3DXVECTOR3 w;

	w = position;

	//VIEW * PROJECTION
	m = VIEW->GetViewMatrix() * VIEW->GetProjectionMatrix();

	//get vectors from view * proj
	s.x = w.x * m._11 + w.y * m._21 + w.z * m._31 + m._41;
	s.y = w.x * m._12 + w.y * m._22 + w.z * m._32 + m._42;
	s.z = w.x * m._13 + w.y * m._23 + w.z * m._33 + m._43;
	s.w = w.x * m._14 + w.y * m._24 + w.z * m._34 + m._44;

	//get inverse of w
	float w_inv = 1.0f / s.w;
	//get center of screen
	float fWidth2 = mBBWidth* 0.5f;
	float fHeight2 = mBBHeight* 0.5f;

	//get screen coordinates
	D3DXVECTOR3 ScreenPosition;
	ScreenPosition.x = (1.0f + (s.x * w_inv)) * fWidth2;
	ScreenPosition.y = (fHeight2*2.0f) - ((1.0f + (s.y * w_inv)) * fHeight2);
	ScreenPosition.z = 0.0f;
	
	return ScreenPosition;
}

// disables persistant text with the given ID
void FloatingTextManager::DisablePersistentText(unsigned int id)
{
	for(Texts::iterator i = textData.begin(); i != textData.end(); ++i)
	{
		if ((*i)->id == id)
		{
			// erase this persistent text objects and return
			delete [TEXT_BUFFERSIZE] (*i)->text;
			delete *i;
			textData.erase(i);
			return;
		}
	}
}